#include <cassert>
#include <iostream>
#include <string>
#include <sstream>
#include <atomic>

#include <pthread.h>
#include <unistd.h>

struct Point {

  Point(): Point(0, 0) {}

  Point(int x, int y) : x(x), y(y) {}

  std::string ToString(){
    std::ostringstream oss; 
    oss << "Point:{" << x << "," << y << "}";
    return oss.str();
  }

  int x;
  int y;
};




class Threads {
private:

  static void * cb_increase_counter(void * arg){
    // get the counter from outside
    Threads * pThis = static_cast<Threads*>(arg);
    if(pThis == nullptr) {
      printf("Error: argument is nullptr when create thread!");
      return nullptr;
    }

    pthread_t tid = pthread_self();

    const int MAX_INC_COUNT = 10;

    for( int i = 0; i < MAX_INC_COUNT; i++) {
      assert(0 == pthread_mutex_lock(&(pThis->mutex)));
      {
        pThis->point.x += 1;
        pThis->point.y += 2;
        printf("thread [0x%04x] increase %s\n", (unsigned int)tid & 0xffff, pThis->point.ToString().c_str());
      }
      assert(0 == pthread_mutex_unlock(&(pThis->mutex)));
      usleep(10 * 1000);
    }

    printf("thread[0x%04x] exit\n", (unsigned int)tid & 0xffff);
    return nullptr;
  }

public:  
  Threads(){
    assert(0 == pthread_mutex_init(&mutex, NULL));
  }
  ~Threads(){
    assert(0 == pthread_mutex_destroy(&mutex));
  }

  void start(){
    for(int i = 0; i < THREAD_NUMBERS; ++i){
      assert(0 == pthread_create(tids+i, NULL, cb_increase_counter, this));
    }
  }

  void join(){
    for(int i =0; i < THREAD_NUMBERS; ++i){
      assert(0 == pthread_join(tids[i], NULL));
    }
  }

private:
  static const int THREAD_NUMBERS = 3;

  pthread_t tids[THREAD_NUMBERS];


  pthread_mutex_t mutex;
  Point point;
};

int main(){
  Threads threads;

  threads.start();
  threads.join();

  pthread_t  tid = pthread_self();
  printf("main  [0x%04x] exit \n",(unsigned int)tid & 0xffff);

  return 0;
}
